/*
add head  and crc to the input data
use fifo to cache the current input data,orgnaiz the head part and read the input data
from fifo
*/
module arp_tx #(
    parameter   SOURCE_IP       = {8'd192,8'd168,8'd7,8'd11}            ,
    parameter   SOURCE_MAC      = 48'h11_22_33_44_55_66                     
)(
    input                       i_clk                                   ,
    input                       i_rst                                   ,
    input   [31: 0]             i_set_source_ip                         ,
    input                       i_set_source_ip_valid                   ,
    input   [47: 0]             i_set_source_mac                        ,
    input                       i_set_source_mac_valid                  ,
    input                       i_arp_req                               ,
    input   [31: 0]             i_arp_req_ip                            ,
    output  [47:0]              o_arp_info_mac0                         ,
    output  [31:0]              o_arp_info_ip0                          ,
    output  [47:0]              o_arp_info_mac1                         ,
    output  [31:0]              o_arp_info_ip1                          ,
    input                       i_arp_rx_valid                          ,
    input   [31: 0]             i_arp_rx_ip                             ,
    input   [47: 0]             i_arp_rx_mac                            ,
    input   [15: 0]             i_arp_rx_type                           ,
    output                      m_axis_tvalid                           ,
    output  [7 : 0]             m_axis_tdata                            ,
    input                       m_axis_tready                           ,
    output                      m_axis_tlast                            ,
    output  [64: 0]             m_axis_tuser                            
);
    localparam  ARP_REQUEST     = 16'h0001                              ;
    localparam  ARP_RESPONSE    = 16'h0002                              ;

    logic   [31: 0]             set_source_ip                           ;
    logic   [47: 0]             set_source_mac                          ;
    logic                       arp_extern_req                          ;
    logic   [31: 0]             arp_extern_req_ip                       ;
    logic                       arp_rx_valid                            ;
    logic   [31: 0]             arp_rx_ip                               ;
    logic   [47: 0]             arp_rx_mac                              ;
    logic   [15:0 ]             arp_rx_type                             ;
    logic   [15: 0]             lsm_cnt                                 ;
    logic   [30: 0]             auto_req_cnt                            ;
    logic                       arp_auto_req                            ;
    logic                       arp_tx_req                              ;
    logic                       arp_tx_req_d                            ;
    logic   [15:0]              arp_tx_type                             ;
    logic   [15:0]              arp_tx_type_d                           ;
    logic   [31: 0]             arp_tx_target_ip                        ;
    logic   [31: 0]             arp_tx_target_ip_d                      ;
    logic                       arp_tx_valid                            ;
    logic   [7 :0]              axis_tx_data                            ;
    logic   [64: 0]             axis_tx_user                            ;
    logic                       axis_tx_last                            ;
    logic                       axis_tx_valid                           ;  
    logic                       tx_is_broadcast                         ;
    logic   [47:0]              arp_info_mac[1:0]                       ;
    logic   [31:0]              arp_info_ip[1:0]                        ;

    assign m_axis_tdata = axis_tx_data;
    assign m_axis_tuser = axis_tx_user;
    assign m_axis_tlast = axis_tx_last;
    assign m_axis_tvalid = axis_tx_valid;

    assign o_arp_info_mac0 =arp_info_mac[0]; 
    assign o_arp_info_ip0 =arp_info_ip[0];
    assign o_arp_info_mac1 =arp_info_mac[1]; 
    assign o_arp_info_ip1 =arp_info_ip[1];



    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            set_source_ip <= SOURCE_IP;
        else if(i_set_source_ip_valid)
            set_source_ip <= i_set_source_ip;
        else 
            set_source_ip <= set_source_ip;
    end 

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            set_source_mac <= SOURCE_MAC;
        else if(i_set_source_mac_valid)
            set_source_mac <= i_set_source_mac;
        else 
            set_source_mac <= set_source_mac;
    end  



    always_ff @( posedge i_clk ) begin
        if(i_rst) begin
            arp_extern_req <= 0;
            arp_extern_req_ip <= 0;
        end
        else begin
            arp_extern_req <= i_arp_req;
            arp_extern_req_ip <= i_arp_req_ip;
        end
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) begin
            arp_info_mac[0] <= 48'hff_ff_ff_ff_ff_ff;
            arp_info_mac[1] <= 48'hff_ff_ff_ff_ff_ff;
            arp_info_ip[0] <= 32'hff_ff_ff_ff;
            arp_info_ip[1] <= 32'hff_ff_ff_ff;
        end
        else if(arp_rx_valid & arp_rx_type == ARP_RESPONSE) begin
            arp_info_mac[0] <= i_arp_rx_mac;
            arp_info_ip[0] <= i_arp_rx_ip;
        end
        else if(arp_rx_valid & arp_rx_type == ARP_REQUEST) begin
            arp_info_mac[1] <= i_arp_rx_mac;
            arp_info_ip[1] <= i_arp_rx_ip;
        end
        else begin
            arp_info_mac[0] <= arp_info_mac[0];
            arp_info_ip[0] <= arp_info_ip[0];
            arp_info_mac[1] <= arp_info_mac[1];
            arp_info_ip[1] <= arp_info_ip[1];
        end
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            auto_req_cnt <= 0;
        else 
            auto_req_cnt <= auto_req_cnt + 1;
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            arp_auto_req <= 0;
        else if(auto_req_cnt == 1000)
            arp_auto_req <= 0;//暂时不用
        else  
            arp_auto_req <= 0;
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) begin
            arp_rx_ip <= SOURCE_IP;
            arp_rx_mac <= 0;
            arp_rx_type <= 0;
            arp_rx_valid <= 0;
        end
        else if(i_arp_rx_valid) begin
            arp_rx_ip <= i_arp_rx_ip;
            arp_rx_mac <= i_arp_rx_mac;
            arp_rx_type <= i_arp_rx_type;
            arp_rx_valid <= 1;
        end
        else begin
            arp_rx_ip <= arp_rx_ip;
            arp_rx_mac <= arp_rx_mac;
            arp_rx_type <= arp_rx_type;
            arp_rx_valid <= 0;
        end
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) begin
            arp_tx_req <= 0;
            arp_tx_type <= 0;
            arp_tx_target_ip <= SOURCE_IP;
            arp_tx_req_d <= 0;
            arp_tx_type_d <= 0;
            arp_tx_target_ip_d <= SOURCE_IP;
            tx_is_broadcast <= 0;
        end
        else if(!arp_tx_req && arp_rx_valid && arp_rx_type == ARP_REQUEST) begin
            if(!axis_tx_valid) begin
                arp_tx_req <= 1;
                arp_tx_type <= ARP_RESPONSE;
                arp_tx_target_ip <= arp_rx_ip;
                tx_is_broadcast <= 0;
            end
            else begin
                arp_tx_req_d <= 1;
                arp_tx_type_d <= ARP_RESPONSE;
                arp_tx_target_ip_d <= arp_rx_ip;
            end
            
        end
        else if(!arp_tx_req && arp_tx_req_d & !axis_tx_valid ) begin
            arp_tx_req <= 1;
            arp_tx_type <= arp_tx_type_d;
            arp_tx_target_ip <= arp_tx_target_ip_d;
            arp_tx_req_d <= 0;
            tx_is_broadcast <= 0;
        end
        else if(!arp_tx_req && arp_extern_req && !axis_tx_valid ) begin
            arp_tx_req <= 1;
            arp_tx_type <= ARP_REQUEST;
            arp_tx_target_ip <= arp_extern_req_ip;
            tx_is_broadcast <= 1;
        end
        else if(!arp_tx_req && arp_auto_req && !axis_tx_valid && arp_info_ip[0] !== 32'hff_ff_ff_ff) begin
            arp_tx_req <= 1;
            arp_tx_type <= ARP_REQUEST;
            arp_tx_target_ip <= arp_info_ip[0];
            tx_is_broadcast <= 1;
        end
        else if(arp_tx_req & m_axis_tready)begin
            arp_tx_req <= 0;
            arp_tx_type <= arp_tx_type;
            arp_tx_target_ip <= arp_tx_target_ip;
            arp_tx_req_d <= arp_tx_req_d;
            arp_tx_type_d <= arp_tx_type_d;
            arp_tx_target_ip_d <= arp_tx_target_ip_d;
            tx_is_broadcast <= tx_is_broadcast;
        end
        else begin
            arp_tx_req <= arp_tx_req;
            arp_tx_type <= arp_tx_type;
            arp_tx_target_ip <= arp_tx_target_ip;
            arp_tx_req_d <= arp_tx_req_d;
            arp_tx_type_d <= arp_tx_type_d;
            arp_tx_target_ip_d <= arp_tx_target_ip_d;
            tx_is_broadcast <= tx_is_broadcast;
        end
    end 



    
    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            lsm_cnt <= 0;
        else if(lsm_cnt==27)
            lsm_cnt <= 0;
        else if(arp_tx_valid)
            lsm_cnt <= lsm_cnt + 1;
        else 
            lsm_cnt <= lsm_cnt;
    end  


    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            arp_tx_valid <= 0;
        else if( lsm_cnt==27 && arp_tx_valid)
            arp_tx_valid <= 0;
        else if(arp_tx_req & m_axis_tready)
            arp_tx_valid <= 1;
        else 
            arp_tx_valid <= arp_tx_valid;
    end  
    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            axis_tx_valid <= 0;
        else 
            axis_tx_valid <= arp_tx_valid;
    end  

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            axis_tx_user <= 0;
        else
            axis_tx_user <= {tx_is_broadcast,arp_tx_target_ip,32'h0806_001C};
    end 

    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            axis_tx_last <= 0;
        else if(axis_tx_valid & (lsm_cnt==27))
            axis_tx_last <= 1;
        else 
            axis_tx_last <= 0;
    end  

    //genarate the 22bytes of mac header 
    always_ff @( posedge i_clk ) begin
        if(i_rst) 
            axis_tx_data <= 0;
        else 
            case (lsm_cnt)
                0 : axis_tx_data <= 8'h00; 
                1 : axis_tx_data <= 8'h01; 
                2 : axis_tx_data <= 8'h08; 
                3 : axis_tx_data <= 8'h00; 
                4 : axis_tx_data <= 8'h06; 
                5 : axis_tx_data <= 8'h04; 
                6 : axis_tx_data <= arp_tx_type[15: 8]; 
                7 : axis_tx_data <= arp_tx_type[7 : 0];
                8 : axis_tx_data <= set_source_mac[47:40];//src_mac
                9 : axis_tx_data <= set_source_mac[39:32];
                10: axis_tx_data <= set_source_mac[31:24];
                11: axis_tx_data <= set_source_mac[23:16];
                12: axis_tx_data <= set_source_mac[15: 8];
                13: axis_tx_data <= set_source_mac[7 : 0];
                14: axis_tx_data <= set_source_ip[31:24];//src_ip
                15: axis_tx_data <= set_source_ip[23:16];
                16: axis_tx_data <= set_source_ip[15: 8];//src_ip
                17: axis_tx_data <= set_source_ip[7 : 0];
                18 :axis_tx_data <= (arp_tx_type==16'h0001) ? 8'h00 : arp_rx_mac[47:40];//src_mac
                19 :axis_tx_data <= (arp_tx_type==16'h0001) ? 8'h00 : arp_rx_mac[39:32];
                20: axis_tx_data <= (arp_tx_type==16'h0001) ? 8'h00 : arp_rx_mac[31:24];
                21: axis_tx_data <= (arp_tx_type==16'h0001) ? 8'h00 : arp_rx_mac[23:16];
                22: axis_tx_data <= (arp_tx_type==16'h0001) ? 8'h00 : arp_rx_mac[15: 8];
                23: axis_tx_data <= (arp_tx_type==16'h0001) ? 8'h00 : arp_rx_mac[7 : 0]; 
                24: axis_tx_data <= arp_tx_target_ip[31:24];//src_ip
                25: axis_tx_data <= arp_tx_target_ip[23:16];
                26: axis_tx_data <= arp_tx_target_ip[15: 8];//src_ip
                27: axis_tx_data <= arp_tx_target_ip[7 : 0];
                default :axis_tx_data <= 0;
            endcase
    end

    
   

endmodule
